//!
/**
 **************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************
 * @file main.cpp
 **************************************************************************************************************************************************************/

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE

/**************************************************************************************************************************************************************
 * Includes
 **************************************************************************************************************************************************************/
#include <RBMain/RBCommon.h>
#include <RBMain/RBMathVector.h>
#include <RBMain/RBEntity.h>
#include <RBMain/RBHandle.h>
#include <RBMain/RBRenderPass.h>

#include <RBMain/RBVertexBuffer.h>
#include <RBMain/RBVertexDeclaration.h>
#include <RBMain/RBVertexElement.h>

#include <RBApplication/RBWin32Application.h>
#include <RBApplication/Main.h>

#include <RBD3D11Renderer/RBD3D11Renderer.h>

#include <RBRenderProcess/RBEffect.h>
#include <RBRenderProcess/RBEffectFactory.h>
#include <RBRenderProcess/RBEffectTechnique.h>
#include <RBMain/RBRenderPassDebug.h>
#include <RBMain/RBRenderPassFullScreen.h>
#include <RBRenderProcess/RBRenderTargetView.h>
#include <RBRenderProcess/RBDepthStencilView.h>

#include <RBTransformSystem/RBTransformComponent.h>

#include <RBMain/RBVisibilityComponent.h>

#include <Miniball.h>

#include <d3d9.h>

#pragma comment(lib, "FCollada.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")

#ifdef _DEBUG
#define PATH_TO_RB_LIB "../../../Lib/Debug"
#else
#define PATH_TO_RB_LIB "../../../Lib/Release"
#endif

#define COMMENT_LIB(Library) comment(lib, XSTR(Library##.lib))
#define COMMENT_RBLIB(Library) COMMENT_LIB(PATH_TO_RB_LIB ## "/" ## Library)

#pragma COMMENT_RBLIB(RBApplication)
#pragma COMMENT_RBLIB(RBD3D11Renderer)
#pragma COMMENT_RBLIB(RBMain)
//#pragma COMMENT_LIB(opengl32)

/**************************************************************************************************************************************************************
 * Defines
 **************************************************************************************************************************************************************/

#define RB_GPU_MARKER(tag) RBGPUMarker maker##__LINE__(L##tag)

class RBGPUMarker
{
public:
        RBGPUMarker(const WCHAR* tag)
        {
            D3DPERF_BeginEvent( D3DCOLOR_XRGB( rand() % 255, rand() % 255, rand() % 255 ), tag );

        }

       ~RBGPUMarker()
       {
           D3DPERF_EndEvent();
       }
};

/**************************************************************************************************************************************************************
 * Classes implementation
 **************************************************************************************************************************************************************/

static const uint32 g_smSizeY = 512;
static const uint32 g_smSizeX= g_smSizeY;
static const uint32 g_splitZCount = 6;

// Max split count
static const uint32 g_splitCount = 32;

static float32 g_aSplits[g_splitZCount] = {3,10,25,45,85, 125};
static RBColor g_aColors[g_splitZCount] = {RBColor(1,0,0),RBColor(0,1,0),RBColor(0,0,1),RBColor(1,1,0), RBColor(0,0,0), RBColor(1,1,1)};


class RBEmptyApplication : public RBWin32Application
{
    RBDeclareRTTI(RBEmptyApplication, RBWin32Application)
public:

                                RBEmptyApplication  ();
    virtual                    ~RBEmptyApplication  ();

    virtual bool                Init                ();

    virtual void                Render              (const Dt& dt);

    void                        SetupFrusta         ( const float4x4& matView, const float3& lightDir );
    void                        SetupSplitSpheres   ();

private:
    RBHandle<RBCamera>          GetShadowCamera     (uint32 idx);
    RBHandle<RBCamera>          GetViewCameraSplit  (uint32 idx);
    void                        InitScene           (RBVisibilityManager* pScene);
    void                        UpdateSplits        ();

    RBHandle<RBEntity>          m_shadowCameras;
    RBHandle<RBEntity>          m_splitCameras;
    RBHandle<RBLight>           m_pSunLight;
    RBHandle<RBCamera>          m_cullingCamera;
    float4x4*                   m_pLightViewProj;
    float4x4*                   m_pInvProj;
    float4*                     m_pCameraParams;
    uint32                      m_currentSplitZ;    
    RBTransform                 m_currentWorldXForm;

    float4                      m_centers[g_splitCount];
    float32                     m_radii[g_splitCount];

	uint32						m_xSplitCount[g_splitZCount];
	uint32						m_ySplitCount[g_splitZCount];

    RBGeometryArray             m_mainViewGeometries;
    RBCuller                    m_mainView;

    RBCuller                    m_shadowCuller;

    RBEntityHandle              m_pBoxEntity;
    RBHandle<RBGeometry>        m_boxes;
    RBVertexBuffer*             m_vb;

    RBRenderTargetGroup*        m_depthMapRTT;
    RBShaderResourceView*       m_depthMap;

    RBRenderTargetGroup*        m_shadowMapRTT;
    RBShaderResourceView*       m_shadowMap;

    RBRenderTargetGroup*        m_grayBufferRTT;
    RBShaderResourceView*       m_grayBuffer;

    RBRenderTargetGroup*        m_linearDepthBufferRTT;
    RBShaderResourceView*       m_linearDepthBuffer;

    RBRenderPassFullScreen*     m_resolveDepth;
    RBRenderPassFullScreen*     m_resolveShadow;

    RBTransform*                m_transforms;


};



/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBEmptyApplication::RBEmptyApplication()
: RBWin32Application(720,720, false)
, m_pSunLight(NULL)
, m_pLightViewProj(NULL)
, m_pInvProj(NULL)
, m_pCameraParams(NULL)
{
    m_splitCameras = RBEntity::CreateEntity();
    RBTransformComponentHandle cameras_xForms[g_splitCount];
    RBCameraHandle cameras[g_splitCount];

    RBTransformComponent::Create(m_splitCameras, cameras_xForms, g_splitCount);
    RBCamera::Create(m_splitCameras, cameras_xForms, cameras, g_splitCount);

    m_splitCameras->AddComponents<RBTransformComponent>(cameras_xForms, g_splitCount);
    m_splitCameras->AddComponents<RBCamera>(cameras, g_splitCount);

    m_shadowCameras = RBEntity::CreateEntity();

    RBTransformComponent::Create(m_shadowCameras, cameras_xForms, g_splitCount);
    RBCamera::Create(m_shadowCameras, cameras_xForms, cameras, g_splitCount);

    m_shadowCameras->AddComponents<RBTransformComponent>(cameras_xForms, g_splitCount);
    m_shadowCameras->AddComponents<RBCamera>(cameras, g_splitCount);

    m_transforms = new RBTransform[g_splitCount];
    m_currentSplitZ = g_splitZCount;

    for (uint32 ui=0; ui < g_splitZCount; ++ui)
    {
        m_xSplitCount[ui] = m_ySplitCount[ui] = 1;
    }
}


/***********************************************************************************************************
 *
 ***********************************************************************************************************/
RBEmptyApplication::~RBEmptyApplication()
{
    delete[] m_transforms;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
bool RBEmptyApplication::Init()
{
    if (!Super::Init())
    {
        return false;
    }


    InitScene(m_pMainScene);
    

    RBString strFile = "Data/testMaya.dae";
    RBStream* pStream = RBStream::Load(strFile);
    RBEntityHandle pEntity = pStream->GetObjectAt(0);
    RBTransformComponent::UpdateEntity(pEntity);
    RBBVolume::UpdateEntity(pEntity);
    OnEntityLoaded(pEntity);
    
    m_pSunLight = RBEntity::Get(pEntity).GetComponent<RBLight>(0);
    
    m_pLightViewProj = RBEffectFactory::GetSingleton()->DataContainer.AddEffectData<float4x4>("LightViewProj", 1);
    
    RBTextureSampler& shadowSampler = RBEffectFactory::GetSingleton()->DataContainer.GetSampler("sShadow");
    shadowSampler.MinFilter = RBSamplerState::SFM_POINT;
    shadowSampler.MagFilter = RBSamplerState::SFM_POINT;
    shadowSampler.MipFilter = RBSamplerState::SFM_POINT;

    delete pStream;

    return true;
    
}

void RenderShadows( RBCuller* pCuller, uint32 uiFrameID, RBRenderer* pRenderer )
{
    RBGeometryArray shadowGeometries;

    RBRenderPass::CollectGeometries(pCuller, uiFrameID, shadowGeometries);

    for (RBGeometryArray::iterator iter=shadowGeometries.begin(); iter != shadowGeometries.end(); )
    {
        if ((*iter)->IsShadowCaster)
        {
            ++iter;
        }
        else
        {
            iter = shadowGeometries.erase(iter);
        }
    }

    for (uint32 ui=0; ui<shadowGeometries.size(); ++ui)
    {
        shadowGeometries[ui]->GetMaterial()->SetTechnique(1);
    }

    void* pNative[16] = {0};
    RBRenderer::GetSingleton()->PSSetShaderResources(0, 16, pNative);

    RBRenderPass::RenderGeometries(pRenderer, shadowGeometries, pCuller->GetCamera());

    for (uint32 ui=0; ui<shadowGeometries.size(); ++ui)
    {
        shadowGeometries[ui]->GetMaterial()->SetTechnique(0);
    }

    shadowGeometries.clear();
}


/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEmptyApplication::InitScene(RBVisibilityManager* pScene)
{
    m_cullingCamera = RBCamera::Create(InvalidHandle, RBTransformComponent::Create(InvalidHandle));

    m_spCamera->CopyMembers(m_cullingCamera);

    m_mainView.SetCamera(m_cullingCamera);
    m_mainView.SetScene(pScene);
    m_shadowCuller.SetScene(pScene);

    m_spCamera->LocalXForm().Translate.y += 1.0f;

    m_depthMapRTT = RBRenderTargetGroup::Create(NULL, RBRenderer::GetSingleton()->GetDepthStencil());
    m_depthMap = RBRenderer::GetSingleton()->GetHBMgr()->CreateShaderResourceView(RBRenderer::GetSingleton()->GetDepthStencil()->GetBuffer(), PF_R24_UNORM_X8_TYPELESS);

    m_shadowMapRTT = RBRenderTargetGroup::Create(g_smSizeX, g_smSizeY, PF_R32_FLOAT, PF_D24_UNORM_S8_UINT, true, false, 1, 0, 1);
    m_shadowMap = RBRenderer::GetSingleton()->GetHBMgr()->CreateShaderResourceView(m_shadowMapRTT->GetRenderTargetView()->GetBuffer());

    RBTexture *pGrayBuffer, *pLinearDepthBuffer;
    RBRenderTargetView *pGrayBufferView, *pLinearDepthBufferView;
    
    RBRenderer::GetSingleton()->GetHBMgr()->CreateRenderTarget(m_uiWindowWidth, m_uiWindowHeight, PF_R8G8B8A8_UNORM, 1, &pGrayBuffer,        &pGrayBufferView,        &m_grayBuffer);
    RBRenderer::GetSingleton()->GetHBMgr()->CreateRenderTarget(m_uiWindowWidth, m_uiWindowHeight, PF_R32_FLOAT,      1, &pLinearDepthBuffer, &pLinearDepthBufferView, &m_linearDepthBuffer);

    m_grayBufferRTT = RBRenderTargetGroup::Create(pGrayBufferView, RBRenderer::GetSingleton()->GetDepthStencil());

    m_linearDepthBufferRTT = RBRenderTargetGroup::Create(pLinearDepthBufferView, NULL);

    m_resolveShadow = new RBRenderPassFullScreen("ResolveShadow.cfx");
    m_resolveDepth = new RBRenderPassFullScreen("ResolveDepth.cfx");
    
    RBEffect* pEffect = RBEffectFactory::GetSingleton()->RetrieveEffect("Dummy.cfx");

    m_pInvProj = m_resolveDepth->GetMaterial()->DataContainer.AddEffectData<float4x4>("InvProj", 1);
    m_pCameraParams = m_resolveShadow->GetMaterial()->DataContainer.AddEffectData<float4>("CameraParams", 1);

    RBEffectFactory::GetSingleton()->DataContainer.AddView("SHADOWBUFFER", m_shadowMap);
    RBEffectFactory::GetSingleton()->DataContainer.GetSampler("SHADOWBUFFER");
    RBEffectFactory::GetSingleton()->DataContainer.AddView("SHADOWSCREENBUFFER", m_grayBuffer);
    RBEffectFactory::GetSingleton()->DataContainer.GetSampler("SHADOWSCREENBUFFER");
    RBEffectFactory::GetSingleton()->DataContainer.AddView("DEPTHBUFFER", m_depthMap);
    RBEffectFactory::GetSingleton()->DataContainer.AddView("LINEARDEPTHBUFFER", m_linearDepthBuffer);
    RBEffectFactory::GetSingleton()->DataContainer.GetSampler("sPoint");
    RBTextureSampler& sPointClamp = RBEffectFactory::GetSingleton()->DataContainer.GetSampler("sPointClamp");
    sPointClamp.U = RBSamplerState::SAM_CLAMP;
    sPointClamp.V = RBSamplerState::SAM_CLAMP;
    sPointClamp.W = RBSamplerState::SAM_CLAMP;

    RBStream *pStream = RBStream::Load("Data/box.dae");
    if (pStream==NULL)
    {
        return;
    }

    m_pBoxEntity = pStream->GetObjectAt(0);
    m_boxes = RBEntity::Get(m_pBoxEntity).GetComponent<RBGeometry>(0);

    m_boxes->GetMaterial()->SetEffect(pEffect);
    m_boxes->IsShadowCaster = false;

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBHandle<RBCamera> RBEmptyApplication::GetViewCameraSplit( uint32 idx )
{
    return m_splitCameras->GetComponent<RBCamera>((RBCameraHandle)(idx));
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBHandle<RBCamera> RBEmptyApplication::GetShadowCamera( uint32 idx )
{
    return m_shadowCameras->GetComponent<RBCamera>((RBCameraHandle)(idx));
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEmptyApplication::SetupFrusta( const float4x4& /*matView*/, const float3& lightDir )
{
    float4 worldCenters[g_splitCount];

    uint32 uiSplit=0;
    for (uint32 uiSplitZ = 0; uiSplitZ < m_currentSplitZ; ++uiSplitZ)
    {
		uint32 splitYCount = m_ySplitCount[uiSplitZ];
		uint32 splitXCount = m_ySplitCount[uiSplitZ];
        for (uint32 uiSplitY = 0; uiSplitY < splitYCount; ++uiSplitY)
        {
		    for (uint32 uiSplitX = 0; uiSplitX < splitXCount; ++uiSplitX)
            {
                RBCamera& camera = *GetViewCameraSplit(uiSplit);
                camera.WorldXForm() = m_currentWorldXForm;
                RBRenderPassDebug::AddFrustum(camera, g_aColors[uiSplitZ]);
                worldCenters[uiSplit] = RBMath::Build(m_currentWorldXForm) * m_centers[uiSplit];

                //RBRenderPassDebug::AddSphere(float3(worldCenters[uiSplit](0),worldCenters[uiSplit](1),worldCenters[uiSplit](2)), m_radii[uiSplit], aColors[uiSplitZ]);
                ++uiSplit;
            }
        }
    }

    // Find where camera should be located among the splits
    static const float nearPlane = 5;
    static const float farPlane = 500;
    static const float startHeight = 250.0f;

    RBPlane shadowCameraPlanePos;
    shadowCameraPlanePos.Build(lightDir, m_currentWorldXForm.Translate - (lightDir * (startHeight)));

    uint32 shadowCamIdx = 0;

    for (uint32 uiSplitZ = 0; uiSplitZ < m_currentSplitZ; ++uiSplitZ)
    {
		uint32 splitYCount = m_ySplitCount[uiSplitZ];
		uint32 splitXCount = m_ySplitCount[uiSplitZ];
        for (uint32 uiSplitY = 0; uiSplitY < splitYCount; ++uiSplitY)
        {
		    for (uint32 uiSplitX = 0; uiSplitX < splitXCount; ++uiSplitX)
            {
                RBHandle<RBCamera> hCamera = GetShadowCamera(shadowCamIdx);
                RBCamera& shadowCamera = *hCamera;
                shadowCamera.WorldXForm().Translate = shadowCameraPlanePos.GetIntersection(worldCenters[shadowCamIdx].To<3>());
                shadowCamera.LookAt(lightDir, float3(0,1,0));
                shadowCamera.SetViewFrustum(-m_radii[shadowCamIdx], m_radii[shadowCamIdx], m_radii[shadowCamIdx], -m_radii[shadowCamIdx], nearPlane, farPlane, true);

                float4x4 projMatrix = RBRenderer::GetSingleton()->BuildProjMatrix(hCamera);
                float4x4 viewMatrix = shadowCamera.GetViewMatrix();

                float4 originShadow = float4(0,0,0,1);
                float4 centerShadowMapCS = projMatrix * viewMatrix * originShadow;
                centerShadowMapCS = centerShadowMapCS * (1.0f / centerShadowMapCS.w);
                float32 x = (centerShadowMapCS.x) * g_smSizeY * 0.5f;
                float32 y = (centerShadowMapCS.y) * g_smSizeY * 0.5f;

                float32 xRounded = (float32)RBMath::Round(x);
                float32 yRounded = (float32)RBMath::Round(y);

                float32 dx = (xRounded - x) / (g_smSizeY * 0.5f);
                float32 dy = (yRounded - y) / (g_smSizeY * 0.5f);

                float4x4 roundTrans = float4x4(1.0f,0.0f,0.0f,dx,
                                               0.0f,1.0f,0.0f,dy,
                                               0.0f,0.0f,1.0f,0.0f,
                                               0.0f,0.0f,0.0f,1.0f);

                float4 topRightNear(1,1,RBRenderer::ms_near,1);
                float4 bottomLeftNear(-1,-1,RBRenderer::ms_near,1);

                float4x4 invLightViewProj = RBMath::InvertMatrix(roundTrans * projMatrix);
                topRightNear = invLightViewProj * topRightNear;
                topRightNear /= topRightNear(3);
                bottomLeftNear = invLightViewProj * bottomLeftNear;
                bottomLeftNear /= bottomLeftNear(3);

                float4 center = 0.5f*(topRightNear + bottomLeftNear);
                dx = center(0);
                dy = center(1);
                RBTransform xForm = shadowCamera.WorldXForm();

                xForm.Translate += dx * shadowCamera.GetWorldRight() + dy * shadowCamera.GetWorldUp();
				shadowCamera.WorldXForm() = xForm;

				RBRenderPassDebug::AddFrustum(shadowCamera, g_aColors[uiSplitZ]);

                xForm.Translate += shadowCamera.GetWorldDirection() * (nearPlane + (farPlane-nearPlane) / 2);
                xForm.Scale = float3(m_radii[shadowCamIdx], m_radii[shadowCamIdx], (farPlane-nearPlane)/2);

                m_transforms[shadowCamIdx] = xForm;

                ++shadowCamIdx;
            }
        }
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEmptyApplication::SetupSplitSpheres()
{
    float3 frustumPos[8];
    uint32 uiSplit=0;

    for (uint32 uiSplitZ = 0; uiSplitZ < m_currentSplitZ; ++uiSplitZ)
    {
		uint32 splitYCount = m_ySplitCount[uiSplitZ];
		uint32 splitXCount = m_ySplitCount[uiSplitZ];
        for (uint32 uiSplitY = 0; uiSplitY < splitYCount; ++uiSplitY)
        {
		    for (uint32 uiSplitX = 0; uiSplitX < splitXCount; ++uiSplitX)
            {
                const RBCamera& camera = *GetViewCameraSplit(uiSplit);
                uint32 uiPoint = 0;
                float3 cameraUp = float3(0,1,0);
                float3 cameraRight = float3(1,0,0);
                float3 cameraDir = float3(0,0,-1);
                frustumPos[uiPoint++] = camera.Near() * (cameraDir + camera.Top()    * cameraUp + camera.Left() * cameraRight);
                frustumPos[uiPoint++] = camera.Near() * (cameraDir + camera.Top()    * cameraUp + camera.Right() * cameraRight);
                frustumPos[uiPoint++] = camera.Near() * (cameraDir + camera.Bottom() * cameraUp + camera.Right() * cameraRight);
                frustumPos[uiPoint++] = camera.Near() * (cameraDir + camera.Bottom() * cameraUp + camera.Left() * cameraRight);

                frustumPos[uiPoint++] = camera.Far() * (cameraDir + camera.Top()    * cameraUp + camera.Left() * cameraRight);
                frustumPos[uiPoint++] = camera.Far() * (cameraDir + camera.Top()    * cameraUp + camera.Right() * cameraRight);
                frustumPos[uiPoint++] = camera.Far() * (cameraDir + camera.Bottom() * cameraUp + camera.Right() * cameraRight);
                frustumPos[uiPoint++] = camera.Far() * (cameraDir + camera.Bottom() * cameraUp + camera.Left() * cameraRight);

                Miniball<3> mb;
                for (uint32 uiPoint=0; uiPoint < 8; ++uiPoint)
                {
                    mb.check_in(Point<3>(&frustumPos[uiPoint].x));
                }
                mb.build();

                Point<3> center = mb.center();
                m_centers[uiSplit] = float4(center[0],center[1],center[2],1);
                m_radii[uiSplit] = sqrtf(mb.squared_radius());

                ++uiSplit;
            }
        }
    }

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEmptyApplication::Render( const Dt& /*dt*/ )
{
    RB_GPU_MARKER("Frame");
    if (RBInput::GetSingleton()->IsReleased(RBInput::IK_R))
    {
        RBEffectFactory::GetSingleton()->Reset(true);
        RBEffectFactory::GetSingleton()->Reset(false);
    }

    UpdateSplits();

    static uint32 uiFrameID = 0;

    RBRenderer* pRenderer = RBRenderer::GetSingleton();

    RBRenderPass::CollectGeometries(&m_mainView, uiFrameID, m_mainViewGeometries);

    for (uint32 ui=0; ui<m_mainViewGeometries.size(); ++ui)
    {
        m_mainViewGeometries[ui]->GetMaterial()->SetTechnique(1);
    }


    {
        RB_GPU_MARKER("Z Prepass");
        // Z Prepass
        RBRenderPass::OpenRenderTargetGroup(pRenderer, m_depthMapRTT);
        RBRenderPass::RenderGeometries(pRenderer, m_mainViewGeometries, m_spCamera);
        m_resolveDepth->Render(pRenderer, m_linearDepthBufferRTT);
    }


    uint32 shadowCamIdx=0;
//    if (shadowCamIdx==1)
    {
        RB_GPU_MARKER("Shadows");
        // Shadow Pass and resolve in white buffer

		float4x4 mainViewMatrix = m_spCamera->GetViewMatrix();
		float4x4 invMainViewMatrix = RBMath::InvertMatrix(mainViewMatrix);

        float32 halfPixelSize = 0.5f / g_smSizeY;
		float4x4 halfOffsetScale = float4x4(0.5f * (1.0f-halfPixelSize), 0.0f,                        0.0f, 0.5f + halfPixelSize,
                                    0.0f,                       -0.5f * (1.0f-halfPixelSize), 0.0f, 0.5f + halfPixelSize,
                                    0.0f,                        0.0f,                        1.0f, -0.0001f,
                                    0.0f,                        0.0f,                        0.0f, 1.0f);

		RBTransform testLightViewProj[g_splitZCount];


        uint8 stencilVal=0;
        for (uint32 uiSplitZ=0; uiSplitZ < m_currentSplitZ; ++uiSplitZ)
        {
			uint32 splitYCount = m_ySplitCount[uiSplitZ];
			uint32 splitXCount = m_ySplitCount[uiSplitZ];
			for (uint32 uiSplitY = 0; uiSplitY < splitYCount; ++uiSplitY)
	        {
			    for (uint32 uiSplitX = 0; uiSplitX < splitXCount; ++uiSplitX)
                {
					RBHandle<RBCamera> hLightCam = GetShadowCamera(shadowCamIdx);

                    {
                        RB_GPU_MARKER("Shadows Generation");
                        RBRenderPass::OpenRenderTargetGroup(pRenderer, m_shadowMapRTT);
                        m_shadowCuller.SetCamera(GetShadowCamera(shadowCamIdx));
                        pRenderer->RSSetViewport(RBViewPort(0.0f,0.0f,(float32)g_smSizeY,(float32)g_smSizeY));
                        RenderShadows(&m_shadowCuller, uiFrameID, pRenderer);
                    }

                    {
                        RB_GPU_MARKER("Shadows White Buffer");
						float4x4 lightViewMatrix = hLightCam->GetViewMatrix();
						float4x4 lightProjMatrix = RBRenderer::GetSingleton()->BuildProjMatrix(hLightCam);
                        *m_pLightViewProj = ( halfOffsetScale * lightProjMatrix * lightViewMatrix * invMainViewMatrix).Transpose();
						testLightViewProj[uiSplitZ] = hLightCam->WorldXForm();

                        uint32 clearColor = (uiSplitZ == 0 ? RBRenderer::CF_COLOR : 0);
                        RBRenderPass::OpenRenderTargetGroup(pRenderer, m_grayBufferRTT, clearColor);

                        m_boxes->WorldXForm() = m_transforms[shadowCamIdx++];



                        RBMaterial* pBoxMat = m_boxes->GetMaterial();
                        pBoxMat->DepthState.ZEnable=true;
                        pBoxMat->DepthState.ZWriteEnable=false;
                        pBoxMat->RasterState.FrontCounterClockwise = false;
                        //pBoxMat->DepthState.ZFunc=RBMaterial::CF_GREATER;

                        pBoxMat->SetTechnique(0);

                        pBoxMat->DepthState.StencilEnable=true;
                        pBoxMat->DepthState.StencilRef=stencilVal;
                        pBoxMat->DepthState.StencilWriteMask=0xFF;
                        pBoxMat->DepthState.StencilFrontFaceFunc=RBMaterial::CF_EQUAL;

                        pBoxMat->DepthState.StencilFrontFacePassOp=RBMaterial::SO_INCR;
                        pBoxMat->DepthState.StencilFrontFaceFailOp=RBMaterial::SO_KEEP;
                        pBoxMat->DepthState.StencilFrontFaceDepthFailOp=RBMaterial::SO_KEEP;

                        RBMaterial* pFullScreenStencilMat = m_resolveShadow->GetMaterial();
                        pFullScreenStencilMat->DepthState.StencilEnable=true;
                        pFullScreenStencilMat->DepthState.StencilRef=stencilVal;
                        pFullScreenStencilMat->DepthState.StencilWriteMask=0x00;
                        pFullScreenStencilMat->DepthState.ZEnable=false;
                        pFullScreenStencilMat->DepthState.StencilFrontFaceFunc=RBMaterial::CF_EQUAL;


                        pRenderer->SetColorWrite(0,0);
                        RBRenderer::GetSingleton()->UpdateCamera(m_spCamera);
                        pRenderer->Render(m_boxes);

                        pRenderer->SetColorWrite(0,RBRenderer::COLOR_WRITE_ALL);


                        m_resolveShadow->Render(pRenderer, m_grayBufferRTT);
                        ++stencilVal;
                    }
                }

            }
        }
    }
	 

    const  RBBVolumeArray& visibleNodes =  m_mainView.GetVisibleEntities(uiFrameID);
    for (uint32 ui=0; ui<visibleNodes.size(); ++ui)
    {
        RBHandle<RBBVolume> node = visibleNodes[ui];
        if (node->GetObjectType() != RBGeometry::GetClassIdx())
        {
            continue;
        }
        RBHandle<RBGeometry> geom = node->GetObjectHandle();
        geom->GetMaterial()->SetTechnique(0);
    }


    {
        RB_GPU_MARKER("Color Pass");
        // Color Pass
        RBRenderPass::OpenRenderTargetGroup(RBRenderer::GetSingleton(), RBRenderer::GetSingleton()->GetDefaultGroup(), RBRenderer::CF_ALL, RBColorA(0,1,1,1));

        uint32 idx = RBInput::GetSingleton()->IsDown(RBInput::IK_Z) ? 0 :
                     RBInput::GetSingleton()->IsDown(RBInput::IK_X) ? 1 :
                     RBInput::GetSingleton()->IsDown(RBInput::IK_C) ? 2 : -1;

        RBCameraHandle hCamera = idx == -1 ? m_spCamera : GetShadowCamera(idx);

        pRenderer->Render(m_boxes);
        RBRenderPass::RenderGeometries(pRenderer, m_mainViewGeometries, hCamera);

    }

    RBRenderer::GetSingleton()->UpdateCamera(m_spCamera);
    RBRenderPassDebug::Render(RBRenderer::GetSingleton());
    ++uiFrameID;
    m_mainViewGeometries.clear();
    m_spRenderer->Swap();
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBEmptyApplication::UpdateSplits()
{
    bool updateFrusta = RBInput::GetSingleton()->IsUp(RBInput::IK_U);

	bool updateSplits = false;
	uint32 currentSplit=1;
    if (RBInput::GetSingleton()->IsReleased(RBInput::IK_P))
    {
        m_xSplitCount[currentSplit] = min(m_xSplitCount[currentSplit]+1,3);
        m_ySplitCount[currentSplit] = min(m_ySplitCount[currentSplit]+1,3);
		updateSplits = true;
    }

    if (RBInput::GetSingleton()->IsReleased(RBInput::IK_O))
    {
        m_xSplitCount[currentSplit] = max(m_xSplitCount[currentSplit]-1,1);
        m_ySplitCount[currentSplit] = max(m_ySplitCount[currentSplit]-1,1);
		updateSplits = true;
    }
	++currentSplit;


	if (RBInput::GetSingleton()->IsReleased(RBInput::IK_L))
    {
        m_xSplitCount[currentSplit] = min(m_xSplitCount[currentSplit]+1,3);
        m_ySplitCount[currentSplit] = min(m_ySplitCount[currentSplit]+1,3);
		updateSplits = true;
    }

    if (RBInput::GetSingleton()->IsReleased(RBInput::IK_K))
    {
        m_xSplitCount[currentSplit] = max(m_xSplitCount[currentSplit]-1,1);
        m_ySplitCount[currentSplit] = max(m_ySplitCount[currentSplit]-1,1);
		updateSplits = true;
    }

	++currentSplit;
	if (RBInput::GetSingleton()->IsReleased(RBInput::IK_M))
    {
        m_xSplitCount[currentSplit] = min(m_xSplitCount[currentSplit]+1,3);
        m_ySplitCount[currentSplit] = min(m_ySplitCount[currentSplit]+1,3);
		updateSplits = true;
    }

    if (RBInput::GetSingleton()->IsReleased(RBInput::IK_N))
    {
        m_xSplitCount[currentSplit] = max(m_xSplitCount[currentSplit]-1,1);
        m_ySplitCount[currentSplit] = max(m_ySplitCount[currentSplit]-1,1);
		updateSplits = true;
    }

    
    if (RBInput::GetSingleton()->IsReleased(RBInput::IK_U))
    {
        m_spCamera->WorldXForm() = m_currentWorldXForm;
    }

    if (updateFrusta)
    {
        m_currentWorldXForm = m_spCamera->WorldXForm();
    }

    m_cullingCamera->WorldXForm() = m_currentWorldXForm;
    {
        uint32 uiSplit=0;

        for (uint32 uiSplitZ = 0; uiSplitZ < m_currentSplitZ; ++uiSplitZ)
		{
            float curNear = (uiSplitZ==0) ? m_spCamera->Near() : g_aSplits[uiSplitZ-1];
            float curFar = g_aSplits[uiSplitZ];
			uint32 splitYCount = m_ySplitCount[uiSplitZ];
			uint32 splitXCount = m_ySplitCount[uiSplitZ];
			float32 fFrustumWidth = (m_spCamera->Right() - m_spCamera->Left()) / splitXCount;
			float32 fFrustumHeight = (m_spCamera->Top() - m_spCamera->Bottom()) / splitYCount;
			float32 curLeft;
			float32 curBottom;

			curBottom = m_spCamera->Bottom();
			for (uint32 uiSplitY = 0; uiSplitY < splitYCount; ++uiSplitY)
			{
				curLeft = m_spCamera->Left();
				for (uint32 uiSplitX = 0; uiSplitX < splitXCount; ++uiSplitX)
				{
					GetViewCameraSplit(uiSplit++)->SetViewFrustum(curLeft, curLeft + fFrustumWidth, curBottom + fFrustumHeight, curBottom, 
					       															curNear, curFar, m_spCamera->Ortho());
					curLeft += fFrustumWidth;
				}
				curBottom += fFrustumHeight;
			}
		}
    }
    static bool bFirst = true;

	if (bFirst || updateSplits)
    {
        SetupSplitSpheres();
        bFirst = false;
    }

    float4x4 matView = RBCamera::LookAtDirection(m_pSunLight->GetDirection(), float3(0,1,0));

    SetupFrusta(matView, m_pSunLight->GetDirection());


    if (m_pInvProj)
    {
        *m_pInvProj = RBMath::InvertMatrix(RBRenderer::GetSingleton()->BuildProjMatrix(m_spCamera)).Transpose();
    }

    if (m_pCameraParams)
    {
        *m_pCameraParams = float4(m_spCamera->Near(), m_spCamera->Near(), 1, 1);
    }

}
MAIN_WIN32(RBEmptyApplication)

/**************************************************************************************************************************************************************/
